package IC.Symbols;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

public abstract class SymbolTable {
	/** map from String to Symbol **/
	protected Map<String, Symbol> entries;
	protected String id;
	protected String name;
	protected SymbolTable parentSymbolTable;
	protected Map<String, SymbolTable> childrenSymbolTables;

	public SymbolTable(String id) {
		this.id = id;
		this.name = id;
		entries = new LinkedHashMap<String,Symbol>();
		childrenSymbolTables =  new LinkedHashMap<String,SymbolTable>();
	}

	public SymbolTable(String id, SymbolTable parent) {
		this.id = id;
		this.name = id;
		this.parentSymbolTable = parent;
		entries = new LinkedHashMap<String,Symbol>();
		childrenSymbolTables =  new LinkedHashMap<String,SymbolTable>();
	}
	
	public SymbolTable(String id, String name) {
		this.id = id;
		this.name = name;
		entries = new LinkedHashMap<String,Symbol>();
		childrenSymbolTables =  new LinkedHashMap<String,SymbolTable>();
	}

	public SymbolTable(String id, String name, SymbolTable parent) {
		this.id = id;
		this.name = name;
		this.parentSymbolTable = parent;
		entries = new LinkedHashMap<String,Symbol>();
		childrenSymbolTables =  new LinkedHashMap<String,SymbolTable>();
	}
	
	public Symbol lookup(String id){
		return lookup(id, true, Integer.MAX_VALUE);
	}
	
	public Symbol lookupMethod(String id){
		return lookupMethod(id, true, Integer.MAX_VALUE);
	}
	
	public Symbol lookup(String id, int searcherOrder){
		return lookup(id, true, searcherOrder);
	}
	
	
	public Symbol lookupMethod(String id, int searcherOrder){
		return lookupMethod(id, true, searcherOrder);
	}

	public Symbol lookup(String id, boolean recursively){
		return lookup(id, recursively, Integer.MAX_VALUE);
	}
	
	public Symbol lookup(String id, boolean recursively, int searcherOrder){
		Symbol symbol = entries.get(id);
		if (recursively && (symbol == null || symbol.getOrder() > searcherOrder) && parentSymbolTable != null){
			symbol = parentSymbolTable.lookup(id, searcherOrder);
		}

		return symbol;
	}
	
	public Symbol lookupMethod(String id, boolean recursively, int searcherOrder){
		Symbol symbol = entries.get(id);
		if ((recursively && (symbol == null || symbol.getOrder() > searcherOrder) && parentSymbolTable != null) 
				|| !(symbol instanceof MethodSymbol)) {
			symbol = parentSymbolTable.lookup(id, searcherOrder);
		}

		return symbol;
	}
	
	
	
	public String getId() {
		return id;
	}
	
	public SymbolTable getParent(){
		return parentSymbolTable;
	}

	public String getDisplayString(){
		return null;
	}
	
	public String getName() {
		return name;
	}
	
	public boolean hasChildren() {
		return childrenSymbolTables.size() > 0;
	}

	public void addChild(String name, SymbolTable childSymbolTable){
		childrenSymbolTables.put(name, childSymbolTable);
	}

	public SymbolTable getChild(String name){
		return childrenSymbolTables.get(name);
	}

	public SymbolTable [] getSortedChildrenSymbolTable(){

		Collection<SymbolTable> symbolTableEntries = childrenSymbolTables.values();

		SymbolTable[] array = new SymbolTable[symbolTableEntries.size()];

		int i=0;
		for (SymbolTable entry : symbolTableEntries){
			array[i] = entry;
			i++;
		}

		return array;
	}

	@Override
	public String toString(){

		StringBuffer stringBuffer = new StringBuffer();

		stringBuffer.append(getDisplayString() + "\n");

		Symbol[] array = entriesToSortedList();

		for (int i = 0; i < array.length; i++){
			if (!(array[i] instanceof RetVarSymbol)){
				stringBuffer.append("\t" + array[i] + "\n");
			}
		}

		if (hasChildren()){
			
			SymbolTable[] symbolTables = getSortedChildrenSymbolTable();
			
			stringBuffer.append("Children tables:");
			for (int i = 0; i < symbolTables.length; i++){
				stringBuffer.append(" " + symbolTables[i].name + ",");
			}
			stringBuffer.deleteCharAt(stringBuffer.length()-1);
			stringBuffer.append("\n\n");
			
			for (int i = 0; i < symbolTables.length; i++){
				stringBuffer.append(symbolTables[i] + "\n");
			}
			stringBuffer.deleteCharAt(stringBuffer.length()-1);
		}

		return stringBuffer.toString();
	}

	private Symbol [] entriesToSortedList(){
		Collection<Symbol> symbolEntries = entries.values();
		Symbol[] array = new Symbol[symbolEntries.size()];

		int i = 0;
		for (Symbol entry : symbolEntries){
			array[i] = entry;
			i++;
		}

		return array;
	}

}